﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Messaging;
using IPCByMessages;
using VideoLink.Utility;

namespace VideoLink.Links
{
    /// <summary>
    /// Заменитель смартлинка, используется для тестирования.
    /// Реализован на Socket'ах, создаётся симметричное соединение "точка-точка".
    /// Для использования необходимо знать сетевое имя или IP-адрес компьютера,
    /// на котором запущено второе приложение, и TCP-порт.
    /// </summary>
    public class FakeLink : ILink
    {
        #region Private fields

        /// <summary>
        /// Имя хоста, к которому подключен канал
        /// </summary>
        private string host;

        /// <summary>
        /// TCP-порт, по которому осуществляется подключение
        /// </summary>
        private int port;

        /// <summary>
        /// Канал связи, используемый для связи
        /// </summary>
        private IMessagesChannel channel;

        #endregion

        /// <summary>
        /// Проверяет, является ли используемый канал связи сервером.
        /// true - если сервер, false - в любом другом случае
        /// </summary>
        internal bool IsServer { get { return channel is SyncServerMesagesChannel; } }

        /// <summary>
        /// Проверяет, активно ли соединение.
        /// (Если соединение с другой стороной присутствует)
        /// </summary>
        public bool Active
        {
            get
            {
                return (channel != null)? channel.Active : false;
            }
        }

        /// <summary>
        /// Создать новый экземпляр класса 
        /// с указанными сетевым именем компьютера другой стороны и
        /// TCP-портом, по которому будет осуществляться соединение
        /// </summary>
        /// <param name="host">
        /// Сетевое имя или IP-адрес компьютера, на котором запущено второе приложение
        /// </param>
        /// <param name="port">
        /// TCP-порт, по которому будет осуществляться подключение
        /// </param>
        public FakeLink(string host, int port)
        {
            this.host = host;
            this.port = port;
        }

        #region ILink Members

        /// <summary>
        /// Открыть подключение.
        /// Необходимо вызывать перед началом передачи.
        /// В данной реализации при вызове открывается TCP-подключение
        /// </summary>
        /// <returns>Код ошибки, возникшей при подключении</returns>
        public int OpenDevice()
        {
            try
            {
                Connect(host, port);
            }
            catch (Exception ex)
            {
                DebugLog.Instance.WriteLine("Connection failed:");
                DebugLog.Instance.WriteLine(ex.ToString());
                return 1;
            }
            return 0;
        }

        /// <summary>
        /// Закрыть подключение и освободить все ресурсы.
        /// После вызова этого метода данный канал непригоден для работы.
        /// </summary>
        /// <returns>Код ошибки, возникшей при отключении</returns>
        public int CloseDevice()
        {
            if (channel == null)
                return 0;

            channel.Close();
            channel = null;
            return 0;
        }

        /// <summary>
        /// Передать файл
        /// </summary>
        /// <param name="fileName">Имя файла, который необходимо передать</param>
        public void SendFile(string fileName)
        {
            if (!Active)
                return;

            channel.SendMessage(Utils.ReadAllFromFile(fileName));

            if (SendStatus != null)
                SendStatus(1, 100);
        }

        /// <summary>
        /// Вызывается при изменении состояния процесса передачи файла.
        /// В данной реализации вызывается только по окончании передачи файла
        /// </summary>
        public event TransieveStatusHandler SendStatus;

        /// <summary>
        /// Вызывается при изменении состояния процесса получения файла.
        /// В данной реализации вызывается только по окончании получения файла
        /// </summary>
        public event TransieveStatusHandler GetStatus;

        /// <summary>
        /// Вызывается при получении нового файла.
        /// На момент вызова данного события файл уже готов к использованию.
        /// </summary>
        public event NewFileHandler NewFileReceived;

        #endregion

        #region Work with channel

        /// <summary>
        /// Подключиться к указанному хосту по указанному порту
        /// </summary>
        /// <param name="host">
        /// Сетевое имя или IP-адрес машины, к которой необходимо подключиться
        /// </param>
        /// <param name="port">
        /// TCP-порт, по которому необходимо подключиться
        /// </param>
        private void Connect(string host, int port)
        {
            channel = MessagesChannelManager.EstablishSyncConnection(host, port);
            channel.MessageReceived += new DataEventHandler(channel_MessageReceived);
            channel.Disconnected += new EventHandler(channel_Disconnected);
        }

        /// <summary>
        /// Обработчик события получения сообщения от channel
        /// </summary>
        /// <param name="sender">-</param>
        /// <param name="e">-</param>
        private void channel_MessageReceived(object sender, DataEventArgs e)
        {
            //перевести содержимое сообщения в файл и вызвать события
            var fileName = Path.GetTempFileName();
            
            Utils.WriteToFile(fileName, e.Data);

            if (NewFileReceived != null)
                NewFileReceived(fileName);
            if (GetStatus != null)
                GetStatus(1, 100);
        }

        /// <summary>
        /// Обработчик события Disconnect объекта channel.
        /// Осуществляет переподключение при разрыве связи
        /// </summary>
        /// <param name="sender">-</param>
        /// <param name="e">-</param>
        private void channel_Disconnected(object sender, EventArgs e)
        {
            if (channel.IsServerSide)
                return;
            channel.MessageReceived -= new DataEventHandler(channel_MessageReceived);
            channel.Disconnected -= new EventHandler(channel_Disconnected);
            channel = null;

            Connect(host, port);
        }
        
        #endregion
    }
}
